#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include "../crypt/crypt.h"
#include "../utils/utils.h"
#include <string.h>
 
#include<sys/socket.h>
#include<arpa/inet.h> // for inet_ntoa()
#include<net/ethernet.h>
#include<netinet/ip_icmp.h>   //Provides declarations for icmp header
#include<netinet/udp.h>   //Provides declarations for udp header
#include<netinet/tcp.h>   //Provides declarations for tcp header
#include<netinet/ip.h>    //Provides declarations for ip header

//FILE *logfile;
struct sockaddr_in source, dest;
char* key;
int SECOND;             
time_t t;


void  ALARMhandler(int sig)
{
        if(!ajoutRegleEnCours)
        {
                time(&t);
                if(listrequest != NULL) 
                {
                        struct Request* elem = listrequest->tete;
                        while(elem != NULL)
                        {
                                if(elem->time+RULE_TIMEOUT <= t)
                                {
                                        if(elem->suivant != NULL)
                                                listrequest->tete = elem->suivant;
                                        else
                                                listrequest->tete = NULL;
                                        delIptableRule(elem);
                                        delPacket(elem->id);
                                        //free(elem->ip);
                                        free(elem);
                                        elem = listrequest->tete;
                                }
                                else
                                        //On met l'élément à null car, si la requete courante est valide, les suivantes, plus récentes, sont forcement valides;
                                        elem = NULL;
                        }
                
                }
        }

        alarm(SECOND);                     /* set alarm for next run   */
        signal(SIGALRM, ALARMhandler);     /* reinstall the handler    */
}

char *str_sub (const char *s, unsigned int start, unsigned int end)
{
   char *new_s = NULL;

   if (s != NULL && start < end)
   {
      new_s = malloc (sizeof (*new_s) * (end - start + 2));
      if (new_s != NULL)
      {
         int i;
         for (i = start; i <= end; i++)
         {
            new_s[i-start] = s[i];
         }
         new_s[i-start] = '\0';
      }
      else
      {
         fprintf (stderr, "Memoire insuffisante\n");
         exit (EXIT_FAILURE);
      }
   }
   return new_s;
}

void pcap_fatal(const char* failed_in, const char* errbuf) {
        printf("Fatal Error in %s: %s\n", failed_in, errbuf);
        exit(1);
}
 
void print_udp_packet(const u_char *Buffer , int Size){
  int i;

  unsigned short iphdrlen;
  struct iphdr *iph = (struct iphdr *)(Buffer +  sizeof(struct ethhdr));
  iphdrlen = iph->ihl*4;
  struct udphdr *udph = (struct udphdr*)(Buffer + iphdrlen  + sizeof(struct ethhdr));
  /* sizeof(udph)=4 ocets est différent de la taille réelle de l'entete udp de 8 octets
   On rajout donc 4 octets */
  int header_size =  sizeof(struct ethhdr) + iphdrlen + sizeof(udph) + 4;

  int taillePayload = Size-header_size + 1;
  char payload[taillePayload];
  
  memcpy(payload, (Buffer+header_size), Size-header_size);
  payload[taillePayload-1] = '\0';
  printf("Lecture du payload...\n");
  //printf("Lecture du payload: %s\n\n", payload);

  /*On sépare l'IV du message */
  char ** payloadDecoupe = split(payload, "=");

  /* On décode l'IV */
  char* iv = (char *) malloc (((strlen(payloadDecoupe[1])/3)*4+8) * sizeof (char));
  int tailleDecodedIV = b64_decode(payloadDecoupe[1], iv);

  /* Décryptage */
  
  char algo[] = MCRYPT_RIJNDAEL_256;
  char mode[] = MCRYPT_CBC;
  MCRYPT td;
  
  int keysize = init_module(&td, algo, mode);
  iv[mcrypt_enc_get_block_size(td)] = '\0';
  
  /* Initialisation pour le décryptage*/
  if(mcrypt_generic_init(td, key, strlen(key), iv) < 0)
    fprintf(stderr, "Erreur lors de l'initialisation du décryptage...\n");

  /*decodage*/
  char* decodedPayload = (char *) malloc (((strlen(payloadDecoupe[0])/3)*4+8) * sizeof (char));
  int tailleDecodedPayload = b64_decode(payloadDecoupe[0], decodedPayload);
  printf ("Payload décodé à partir de la base 64...\nEnvoi pour décryptage...\n"); 
  
  /*decryptage*/
  char *decrypted = decrypt_block(&td, decodedPayload, tailleDecodedPayload);
  
  /*On enelève les séparateur*/
  char ** test = split(decrypted, "|");
  
  /* Extraction du hash*/
  unsigned char * hashage = (unsigned char *) test[HASH_POS];
  char* decodedhashage = (char *) malloc (((strlen(hashage)/3)*4+8) * sizeof (char));
  int tailleDecodedhashage = b64_decode(hashage, decodedhashage);
  //printf("Hash récupéré: ");
  //afficheHash(MHASH_MD5, decodedhashage);
  
  /* test du hash */
  char * do_hash = reverse_split(test); //on refait la chaine pour le hachage
  
  int taille_hash;
  unsigned char * hashPayload = hash(MHASH_MD5, do_hash, &taille_hash);
  int test_add;
  printf("Comparaison des Hashages...\n\n");
  if (memcmp(hashPayload, decodedhashage, taille_hash) == 0){ //comparaison des deux hash
    printf("Demande d'autorisation en cours...\n\n");
    int value = add_packet(atoi(test[1]), hashPayload, taille_hash);
    switch(value)
      {
      case 0:
        addRequest(atoi(test[1]),test[0],test[3]);
        printf("Les services demandés sont ouverts pendant %d secondes\n\n", RULE_TIMEOUT);
        break;
      case 1:
        printf("Erreur X0934CF: packet rejoué\n\n");
        break;
      case 2:
        printf("Erreur X0946BZ: packet périmé\n\n");
      break;
      }
     idRequest++;
  }
  
  /*  
  if(list != NULL)
        printf("Nombre de requetes stockées: %d\n\n", list->nb_elem);
  else
        printf("Nombre de requetes stockées: %d\n\n", 0);
  */
  
  free(do_hash);
  free(decodedPayload);
  free(decrypted);
  free(decodedhashage);
  free(iv);
  for(i=0; i<=HASH_POS; i++)
    free(test[i]);
  free(test);
  close_module(&td);
  printf("\n\n");       
}


void process_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *buffer){
        printf("Reception d'un paquet...");
        int size = header->len;
        print_udp_packet(buffer , size);
}


int main(int argc, char *argv[]) {
  if (argc < 2){
    printf("Usage: %s <interface>\n", argv[0]);
    return 0;
  }
  idRequest = 0;
  struct pcap_pkthdr header;
  const u_char *packet;
  char errbuf[PCAP_ERRBUF_SIZE];
  char *device = argv[1];
  bpf_u_int32 net; /* ip */
  bpf_u_int32 mask;/* subnet mask */
  struct bpf_program fp; //compiled filter expression
  char filter_exp[]= "udp and dst port 10000";
  pcap_t *pcap_handle;
  int i;
 
  key = readKey();
  if(key == NULL)
        return 0;
  
  if(device == NULL)
    pcap_fatal("pcap_lookupdev", errbuf);
  
  if (pcap_lookupnet(device,&net, &mask, errbuf) ==-1){
    fprintf(stderr, "Can't get netmask for device %s\n",device);
    net=0;
    mask=0;
  }
  
  printf("Sniffing on device %s\n\n", device);
  pcap_handle = pcap_open_live(device, BUFSIZ, 1, -1, errbuf);
  if(pcap_handle == NULL)
    pcap_fatal("pcap_open_live", errbuf);
  
  if (pcap_compile(pcap_handle, &fp, filter_exp, 0, net)==-1){
    fprintf(stderr, "Couldn't parse filter %s: %s\n",filter_exp,pcap_geterr(pcap_handle));
    return(2);
  }
  
  if (pcap_setfilter(pcap_handle,&fp)==-1){
    fprintf(stderr, "Couldn't install filter %s: %s\n",filter_exp,pcap_geterr(pcap_handle));
    return(2);
  }
 

  SECOND  = 1;
  signal(SIGALRM,ALARMhandler);
  alarm(SECOND);

  pcap_loop(pcap_handle, -1, process_packet, NULL);
  
  
  pcap_close(pcap_handle);
  
  return 0;
}

